package jwtoken

import (
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"main/app"
	"main/config"
	"main/internal/model"
	"main/internal/pkg/code"
	"main/internal/pkg/padding"
	"net/http"
	"time"
)

type UserInfoClaims struct {
	UserId   uint   `json:"user_id"`   // 用户ID
	UserMail string `json:"user_mail"` // 用户邮箱
	jwt.StandardClaims
}

// 生成JWT
func GenerateJwt(userid uint, usermail string, expiresAt time.Duration) (string, error) {
	claims := UserInfoClaims{
		UserId:   userid,
		UserMail: usermail,
		StandardClaims: jwt.StandardClaims{
			NotBefore: time.Now().Unix(),                // 生效时间
			IssuedAt:  time.Now().Unix(),                // 签发日期
			Issuer:    "锋芒万创",                           //签发者
			ExpiresAt: time.Now().Add(expiresAt).Unix(), // 过期时间
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	ss, err := token.SignedString([]byte(config.Cfg.Jwt.Secret))
	if err != nil {
		return "", err
	}
	//加密JWT
	encode, err := padding.PKCS5PaddingEncode(ss, config.Cfg.Jwt.Secret)
	if err != nil {
		return "", err
	}
	return encode, nil
}

// 解析JWT内的用户数据
func ParseJwt(jwtoken string) (bool, *UserInfoClaims, error) {
	token, err := jwt.ParseWithClaims(jwtoken, &UserInfoClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(config.Cfg.Jwt.Secret), nil
	})
	if claims, ok := token.Claims.(*UserInfoClaims); ok && token.Valid {
		return true, claims, nil
	} else {
		return false, claims, err
	}
}

func JwtAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		//先获取JWT
		jwtoken := c.GetHeader("Authorization")
		lang, _ := c.GetQuery("lang")
		if jwtoken == "" {
			c.JSON(http.StatusOK, model.Fail(code.GetMsg(code.JwtAuthFail, lang)))
			c.Abort()
			return
		}
		//解密JWT
		decode, err := padding.PKCS5PaddingDecode(jwtoken, config.Cfg.Jwt.Secret)
		if err != nil {
			c.JSON(http.StatusOK, model.Fail(code.GetMsg(code.JwtAuthFail, lang)+" "+err.Error()))
			c.Abort()
			return
		}
		//解析JWT
		status, claims, err := ParseJwt(decode)
		if err != nil || !status {
			c.JSON(http.StatusOK, model.Fail(code.GetMsg(code.JwtAuthFail, lang)+" "+err.Error()))
			c.Abort()
			return
		}

		var expiration time.Time
		app.Db.Table(model.SysAdmin{}.TableName()).Where("id = ?", claims.UserId).Select("admin_expiration").Find(&expiration)
		if expiration.Unix() > claims.ExpiresAt {
			c.JSON(http.StatusOK, model.Fail(code.GetMsg(code.JwtExpired, lang)))
			c.Abort()
			return
		}
		//设置用户信息到gin.context中
		c.Set("user_id", claims.UserId)
		c.Set("user_mail", claims.UserMail)
		// 判断是还有多久过期，如果小于20分钟那么就颁发新的token
		if claims.ExpiresAt-time.Now().Unix() < 1200 {
			generateJwt, _ := GenerateJwt(claims.UserId, claims.UserMail, 20*time.Minute)
			c.Header("Authorization", generateJwt)
		}
		//走
		c.Next()
	}
}
